﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Reflection;
using Autofac.Extensions.DependencyInjection;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using OpenAuth.App;
using OpenAuth.Repository;
using OpenAuth.WebApi.Model;
using Swashbuckle.AspNetCore.Swagger;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Constraints;
using OpenAuth.Repository.Domain;

namespace OpenAuth.WebApi
{
    public class Startup
    {
        //依赖注入，IConfiguration用于读取配置文件
        public Startup(IConfiguration configuration, IOptions<AppSetting> appConfiguration)
        {
            Configuration = configuration;
            _appConfiguration = appConfiguration;
        }

        public IConfiguration Configuration { get; }
        public IOptions<AppSetting> _appConfiguration;

        // 此方法由运行时调用。使用这种方法将服务添加到容器中
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {

            var identityServer = ((ConfigurationSection)Configuration.GetSection("AppSetting:IdentityServerUrl")).Value;
            if (!string.IsNullOrEmpty(identityServer))
            {
                services.AddAuthorization();

                services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                    .AddJwtBearer(options =>
                    {
                        options.Authority = identityServer;
                        options.RequireHttpsMetadata = false;  // 指定是否为HTTPS
                        options.Audience = "openauthapi";
                    });
            }


            services.AddSwaggerGen(option =>
            {
                option.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
                {
                    Version = "v1",
                    Title = " HisApi接口文档",
                    Description = "by 河北华发"
                });

                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                option.IncludeXmlComments(xmlPath);
                option.OperationFilter<GlobalHttpHeaderOperationFilter>(); // 添加httpHeader参数

                if (!string.IsNullOrEmpty(identityServer))
                {
                    //接入identityserver
                    option.AddSecurityDefinition("oauth2", new OAuth2Scheme
                    {
                        Flow = "implicit", // 只需通过浏览器获取令牌（适用于swagger）
                        AuthorizationUrl = $"{identityServer}/connect/authorize",//获取登录授权接口
                        Scopes = new Dictionary<string, string> {
                            { "openauthapi", "同意openauth.webapi 的访问权限" }//指定客户端请求的api作用域。 如果为空，则客户端无法访问
                        }
                    });
                    option.OperationFilter<AuthResponsesOperationFilter>();
                }


            });
            //
            services.AddCors();
            services.Configure<AppSetting>(Configuration.GetSection("AppSetting"));
            services.Configure<BankParam>(Configuration.GetSection("Bank"));
            services.Configure<BankPlus>(Configuration.GetSection("BankApi"));

            services.AddMvc(config =>
            {
                config.Filters.Add<OpenAuthFilter>();
                config.Filters.Add<HttpGlobalExceptionFilter>();
                config.Filters.Add<GlobalActionFilter>();
            }).AddControllersAsServices().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            //将SuppressModelStateInvalidFilter设置为true后可以禁用自动HTTP 400的行为： 防止The input was not valid
            //services.Configure<ApiBehaviorOptions>(options =>
            //{
            //    options.SuppressModelStateInvalidFilter = true;
            //});
            services.AddMemoryCache();
          
            //在startup里面只能通过这种方式获取到appsettings里面的值，不能用IOptions😰
            var dbType = ((ConfigurationSection)Configuration.GetSection("AppSetting:DbType")).Value;
            if (dbType == Define.DBTYPE_SQLSERVER)
            {
                services.AddDbContext<OpenAuthDBContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("OpenAuthDBContext")));
                //附加卫宁his库的链接上下文
                services.AddDbContext<ApiDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ApiDBContext")));
                //分诊客户端用
                services.AddDbContext<TriageDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("TriageDBContext")));
                services.AddDbContext<CisDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("CisDBContext")));
                services.AddDbContext<YinDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("YinDBContext")));



            }
            else  //mysql
            {
                services.AddDbContext<OpenAuthDBContext>(options =>
                    options.UseMySql(Configuration.GetConnectionString("OpenAuthDBContext")));
            }

            services.AddHttpClient();


            return new AutofacServiceProvider(AutofacExt.InitAutofac(services));//第三方ioc接管内置di容器
        }

        // 此方法由运行时调用，使用这种方法将服务添加到容器中
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                //异常处理添加到默认页
                app.UseDeveloperExceptionPage();
            }

            //可以访问根目录下面的静态文件
            var staticfile = new StaticFileOptions { FileProvider = new PhysicalFileProvider(AppContext.BaseDirectory) };
            app.UseStaticFiles(staticfile);

            //todo:测试可以允许任意跨域，正式环境要加权限
            //app.UseCors(builder => builder.AllowAnyOrigin()
            //    .AllowAnyMethod()
            //    .AllowAnyHeader());


            app.UseAuthentication();
            app.UseSwagger();

            // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 
            // specifying the Swagger JSON endpoint.
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs");
                c.DocExpansion(DocExpansion.None);
                c.OAuthClientId("OpenAuth.WebApi");  //oauth客户端名称
                c.OAuthAppName("客户端为OpenAuth.WebApi"); // 描述
            });

            app.UseCors(builder => builder
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());

            //路由的匹配顺序是按照我们定义的顺序从上之下匹配的，遵循是的先配置先生效的原则。
            app.UseMvc(routes =>

            {
                //name:路由名称，不可重复
                //template：路由模板，可在模板中以{name}格式定义路由参数
                //defaults：配置路由参数默认值
                //constraints:路由约束

                //routes.MapRoute(
                //    name: "default",
                //    template: "api/{controller=Home}/{action=Index}/{id?}");

                //{id?}为可选参数
                //routes.MapRoute(
                //    name: "default",
                //    template: "{Areas=HisApi}/{controller=default}/{action=Index}"
                //        //设置默认路径写法
                //        //defaults: new { controller = "default", action = "index" }
                //        );

                #region 路由笔记           
                //https://mp.weixin.qq.com/s/RrSZTDRPTa9ziUFJ7RQI3Q
                #region 1.1固定前缀的路由
                //1.1固定前缀的路由  由于路径参数中不包含controller参数，所以需要在默认值中指定。
                //routes.MapRoute(
                //    name: "TutorialPrefixRoute",
                //    template: "jiaocheng/{action}",
                //    defaults: new { controller = "Tutorial" }
                // );
                //此路由适配URL：
                /// jiaocheng / index
                /// jiaocheng / welcome
                #endregion

                #region 1.2固定后缀路由
                //1.2固定后缀路由 固定后缀的路由适用于伪静态等诉求
                //routes.MapRoute(
                //    name: "TutorialSuffixRoute",
                //    template: "{controller}/{action}.html"
                //);

                //此路由适配URL：
                /// tutorial / index.html
                /// tutorial / welcome.html
                /// home / index.html
                /// home / time.html
                #endregion

                #region 路由约束
                //路由约束主要是用于约束路由参数，在URL格式满足路有模板要求之后，进行参数检查。如果参数不满足路由约束，那么依然会返回未匹配该路由。
                routes.MapRoute(
                    name: "yueshu",
                    template: "HisApi/{name}/{id?}",
                    defaults: new { controller = "default", action = "age", name = "ken" },
                    constraints: new { name = new MaxLengthRouteConstraint(5) }
                    );

                #endregion

                #region 集中式路由
                //前面章节提到的路由配置都是在Startup类中进行的集中式路由配置，集中配置的路由，除了template中没有配置{ controller}
                //参数，默认都是对所有控制器（Controller）生效的。
                //这种集中配置的方式一般我们只要配置一个默认路由，其他情况我们只需要不满足默认模板的情况下进行配置即可。尤其是对URL没有友好度要求的应用，例如：后台管理系统

                #endregion

                #region 绑定式路由
                //对于集中式路由配置的方式，如果某个Controller / Action配置了特殊路由，对于代码阅读就会不太友好。不过没关系，ASP.NET Core MVC也提供了RouteAttribute可以让我们在Controller或者Action上直接指定路由模板。
                //不过要强调的是，一个控制器只能选择其中一种路由配置，如果控制器标记了RouteAttribute进行路由配置，那么集中式配置的路由将不对其生效。
                //[Route("servertime")]
                //[Route("/t/t")]
                #endregion

                #endregion

            });









        }
    }
}

//在ASP.NET Core应用程序中，使用一个按约定Startup命名的类Startup，在Program.cs中使用WebHostBuilderExtensions UseStartup<TStartup>方法指定类，但通常使用系统默认的startup，可以通过startup的构造函数进行依赖注入，startup类中必须包含Configure方法同时可以根据实际情况添加ConfigureServices方法，这两个方法均在应用程序运行时被调用。Startup 类的 执行顺序：构造 -> configureServices ->configure

//ConfigureServices方法：主要用于服务配置，比如依赖注入(DI) 的配置，使用时该方法必须在Configure方法之前

//Configure方法：用于应用程序响应HTTP请求，通过向IApplicationBuilder实例添加中间件组件来配置请求管道
